package top.lihope.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * @description: 日志切面
 * @ClassName: LoggerAspect
 * @author: 求求别学了，我跟不上
 * @email: xiwang.li@qq.com
 * @date： 2023/4/13 10:32
 * @version： 1.0
 */

@Aspect // 把LoggerAspect类声明为一个切面
@Component //把LoggerAspect类放到IOC容器中
@Order(value = 1) //设置切面的优先级：如果有多个切面，可通过设置优先级控制切面的执行顺序（数值越小，优先级越高）
@Slf4j
public class LoggerAspect {


    /**
     * 定义一个方法，用于声明切入点表达式，方法中一般不需要添加其他代码
     * 使用@Pointcut声明切入点表达式
     * 后面的通知直接使用方法名来引用当前的切点表达式；如果是其他类使用，加上包名即可
     */
    @Pointcut("execution(* top.lihope.service.impl.*.*(..))")
    public void loggerAspectAOP(){}

    /**
     * 前置通知
     * @param joinPoint
     */
    @Before("loggerAspectAOP()") //该标签声明次方法是一个前置通知：在目标方法开始之前执行
    public void beforMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        log.debug("【前置通知】方法名：{}，参数：{}",methodName, args);
    }
    /**
     * 后置通知（无论方法是否发生异常都会执行,所以访问不到方法的返回值）
     * @param joinPoint
     */
    @After("loggerAspectAOP()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        log.debug("【后置通知】方法名：{}", methodName);
    }
    /**
     * 返回通知（在方法正常结束执行的代码）
     * 返回通知可以访问到方法的返回值！
     * @param joinPoint
     */
    @AfterReturning(value="loggerAspectAOP()",returning="result")
    public void afterReturnMethod(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        log.debug("【返回通知】方法名：{}，返回值：{}", methodName, result);
    }
    /**
     * 异常通知（方法发生异常执行的代码）
     * 可以访问到异常对象；且可以指定在出现特定异常时执行的代码
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing(value="loggerAspectAOP()",throwing="ex")
    public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
        String methodName = joinPoint.getSignature().getName();
        log.error("【异常通知】方法名：{}，异常：{}",methodName, ex);
    }
    /**
     * 环绕通知(需要携带类型为ProceedingJoinPoint类型的参数)
     * 环绕通知包含前置、后置、返回、异常通知；ProceedingJoinPoin 类型的参数可以决定是否执行目标方法
     * 且环绕通知必须有返回值，返回值即目标方法的返回值
     * @param point
     */
//    @Around(value="loggerAspectAOP()")
//    public Object aroundMethod(ProceedingJoinPoint point){
//
//        Object result = null;
//        String methodName = point.getSignature().getName();
//        try {
//            //前置通知
//            log.debug("【前置通知】方法名：{}，参数：{}", methodName, Arrays.asList(point.getArgs()));
//            //执行目标方法
//            result = point.proceed();
//            //返回通知
//            log.debug("【返回通知】方法名：{}，返回值：{}",methodName, result);
//        } catch (Throwable e) {
//            //异常通知
//            log.error("【异常通知】方法名：{}，异常：{}",methodName, e);
//            throw new RuntimeException(e);
//        }
//        //后置通知
//        log.debug("【后置通知】方法名：{}", methodName);
//        return result;
//    }
}
